home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.misc
- subject: v13i049: Emacs forms mode 1.1 - part 01 of 03
- from: jv@mh.nl (Johan Vromans)
- Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
-
- Posting-number: Volume 13, Issue 49
- Submitted-by: jv@mh.nl (Johan Vromans)
- Archive-name: forms.el/part01
-
- This is the first public release of GNU Emacs 'forms-mode'.
-
- This GNU Emacs major mode implements editing a structured file (i.e. a
- file with 'records' and 'fields' in it) using a forms.
- It is fully documented in the source file 'forms.el' and in the
- texinfo file 'forms.ti'.
-
- ---- Cut Here and unpack ----
- #!/bin/sh
- # This is a shell archive (shar 3.24)
- # made 06/10/1990 10:33 UTC by jv@squirrel
- # Source directory /u/jv/elisp/src/forms-mode
- #
- # existing files WILL be overwritten
- #
- # This is part 1 of a multipart archive
- # do not concatenate these parts, unpack them in order with /bin/sh
- #
- # This shar contains:
- # length mode name
- # ------ ---------- ------------------------------------------
- # 860 -rw-r--r-- README
- # 36688 -r--r--r-- forms.el
- # 21254 -r--r--r-- forms.ti
- # 436 -rw-r--r-- demo1
- # 475 -rw-r--r-- demo2
- # 476 -rw-r--r-- demo2.dat
- #
- if touch 2>&1 | fgrep '[-amc]' > /dev/null
- then TOUCH=touch
- else TOUCH=true
- fi
- if test -r shar3_seq_.tmp; then
- echo "Must unpack archives in sequence!"
- next=`cat shar3_seq_.tmp`; echo "Please unpack part $next next"
- exit 1
- fi
- # ============= README ==============
- echo "x - extracting README (Text)"
- sed 's/^X//' << 'SHAR_EOF' > README &&
- XThis is the first public release GNU Emacs 'forms-mode'.
- X
- XThis GNU Emacs major mode implements editing a structured file (i.e. a
- Xfile with 'records' and 'fields' in it) using a forms.
- XIt is fully documented in the source file 'forms.el' and in the
- Xtexinfo file 'forms.ti'.
- X
- XThis kit contains:
- X
- X README - this file
- X MANIFEST - list of files
- X forms.el - the lisp source
- X forms.ti - texinfo file
- X demo1 - demo using /etc/passwd
- X demo2 - demo using 'demo2.dat'
- X demo2.dat - data for demo2
- X
- XLoad the lisp source, and execute
- X
- X forms-find-file demo1
- X
- Xto look at your password file in a unconventional (but read-only) way.
- X
- X forms-find-file demo2
- X
- Xgives you something to clobber with data and multi-line fields.
- X
- XThis program has been donated to the Free Software Foundation to be
- Xpart of their GNU Emacs programming system.
- X
- XHave fun!
- X
- X Johan Vromans <jv@mh.nl>
- SHAR_EOF
- $TOUCH -am 0610120990 README &&
- chmod 0644 README ||
- echo "restore of README failed"
- set `wc -c README`;Wc_c=$1
- if test "$Wc_c" != "860"; then
- echo original size 860, current size $Wc_c
- fi
- # ============= forms.el ==============
- echo "x - extracting forms.el (Text)"
- sed 's/^X//' << 'SHAR_EOF' > forms.el &&
- X;;; Forms Mode - A GNU Emacs Major Mode ; @(#)@ forms 1.1.2
- X;;; Created 1989 - Johan Vromans <jv@mh.nl>
- X;;;
- X;;; This file is part of GNU Emacs.
- X
- X;;; GNU Emacs is distributed in the hope that it will be useful,
- X;;; but WITHOUT ANY WARRANTY. No author or distributor
- X;;; accepts responsibility to anyone for the consequences of using it
- X;;; or for whether it serves any particular purpose or works at all,
- X;;; unless he says so in writing. Refer to the GNU Emacs General Public
- X;;; License for full details.
- X
- X;;; Everyone is granted permission to copy, modify and redistribute
- X;;; GNU Emacs, but only under the conditions described in the
- X;;; GNU Emacs General Public License. A copy of this license is
- X;;; supposed to have been given to you along with GNU Emacs so you
- X;;; can know your rights and responsibilities.
- X;;; If you don't have this copy, write to the Free Software
- X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X;;;
- X
- X(provide 'forms-mode) ; Version 1.1.2
- X
- X;;; Visit a file using a form.
- X;;;
- X;;; === Naming conventions
- X;;;
- X;;; The names of all variables and functions start with 'form-'.
- X;;; Names which start with 'form--' are intended for internal use, and
- X;;; should *NOT* be used from the outside.
- X;;;
- X;;; All variables are buffer-local, to enable multiple forms visits
- X;;; simultaneously.
- X;;; Variable 'forms--mode-setup' is local to *ALL* buffers, for it
- X;;; controls if forms-mode has been enabled in a buffer.
- X;;;
- X;;; === How it works ===
- X;;;
- X;;; Forms mode means visiting a data file which is supposed to consist
- X;;; of records each containing a number of fields. The records are
- X;;; separated by a newline, the fields are separated by a user-defined
- X;;; field separater (default: TAB).
- X;;; When shown, a record is transferred to an emacs buffer and
- X;;; presented using a user-defined form. One record is shown at a
- X;;; time.
- X;;;
- X;;; Forms mode is a composite mode. It involves two files, and two
- X;;; buffers.
- X;;; The first file, called the control file, defines the name of the
- X;;; data file and the forms format. This file buffer will be used to
- X;;; present the forms.
- X;;; The second file holds the actual data. The buffer of this file
- X;;; will be buried, for it is never accessed directly.
- X;;;
- X;;; Forms mode is invoked using "forms-find-file control-file".
- X;;; Alternativily forms-find-file-other-window can be used.
- X;;;
- X;;; You may also visit the control file, and switch to forms mode by hand
- X;;; with M-x forms-mode .
- X;;;
- X;;; Automatic mode switching is supported, so you may use "find-file"
- X;;; if you specify "-*- forms -*-" in the first line of the control file.
- X;;;
- X;;; The control file is visited, evaluated using
- X;;; eval-current-buffer, and should set at least the following
- X;;; variables:
- X;;;
- X;;; forms-file [string] the name of the data file.
- X;;;
- X;;; forms-number-of-fields [integer]
- X;;; The number of fields in each record.
- X;;;
- X;;; forms-format-list [list] formatting instructions.
- X;;;
- X;;; The forms-format-list should be a list, each element containing
- X;;;
- X;;; - either a string, e.g. "hello" (which is inserted \"as is\"),
- X;;;
- X;;; - an integer, denoting a field number. The contents of the field
- X;;; are inserted at this point.
- X;;; The first field has number one.
- X;;;
- X;;; Optional variables which may be set in the control file:
- X;;;
- X;;; forms-field-sep [string, default TAB]
- X;;; The field separator used to separate the
- X;;; fields in the data file. It may be a string.
- X;;;
- X;;; forms-read-only [bool, default nil]
- X;;; 't' means that the data file is visited read-only.
- X;;; If no write access to the data file is
- X;;; possible, read-only mode is enforced.
- X;;;
- X;;; forms-multi-line [string, default "^K"]
- X;;; If non-null the records of the data file may
- X;;; contain fields which span multiple lines in
- X;;; the form.
- X;;; This variable denoted the separator character
- X;;; to be used for this purpose. Upon display, all
- X;;; occurrencies of this character are translated
- X;;; to newlines. Upon storage they are translated
- X;;; back to the separator.
- X;;;
- X;;; forms-forms-scroll [bool, default t]
- X;;; If non-nil: redefine scroll-up/down to perform
- X;;; forms-next/prev-field if in forms mode.
- X;;;
- X;;; forms-forms-jump [bool, default t]
- X;;; If non-nil: redefine beginning/end-of-buffer
- X;;; to performs forms-first/last-field if in
- X;;; forms mode.
- X;;;
- X;;; After evaluating the control file, its buffer is cleared and used
- X;;; for further processing.
- X;;; The data file (as designated by "forms-file") is visited in a buffer
- X;;; (forms--file-buffer) which will not normally be shown.
- X;;; Great malfunctioning may be expected if this file/buffer is modified
- X;;; outside of this package while it's being visited!
- X;;;
- X;;; A record from the data file is transferred from the data file,
- X;;; split into fields (into forms--the-record-list), and displayed using
- X;;; the specs in forms-format-list.
- X;;; A format routine 'forms--format' is build upon startup to format
- X;;; the records.
- X;;;
- X;;; When a form is changed the record is updated as soon as this form
- X;;; is left. The contents of the form are parsed using forms-format-list,
- X;;; and the fields which are deduced from the form are modified. So,
- X;;; fields not shown on the forms retain their origional values.
- X;;; The newly formed record and replaces the contents of the
- X;;; old record in forms--file-buffer.
- X;;; A parse routine 'forms--parser' is build upon startup to parse
- X;;; the records.
- X;;;
- X;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save
- X;;; (which doesn't). However, if forms-exit-no-save is executed and the file
- X;;; buffer has been modified, emacs will ask questions.
- X;;;
- X;;; Other functions are:
- X;;;
- X;;; paging (forward, backward) by record
- X;;; jumping (first, last, random number)
- X;;; searching
- X;;; creating and deleting records
- X;;; reverting the form (NOT the file buffer)
- X;;; switching edit <-> view mode v.v.
- X;;; jumping from field to field
- X;;;
- X;;; As an documented side-effect: jumping to the last record in the
- X;;; file (using forms-last-record) will adjust forms--total-records if
- X;;; needed.
- X;;;
- X;;; Commands and keymaps:
- X;;;
- X;;; A local keymap 'forms-mode-map' is used in the forms buffer.
- X;;; As conventional, this map can be accessed with C-c prefix.
- X;;; In read-only mode, the C-c prefix must be omitted.
- X;;;
- X;;; Default bindings:
- X;;;
- X;;; \C-c forms-mode-map
- X;;; TAB forms-next-field
- X;;; SPC forms-next-record
- X;;; < forms-first-record
- X;;; > forms-last-record
- X;;; ? describe-mode
- X;;; d forms-delete-record
- X;;; e forms-edit-mode
- X;;; i forms-insert-record
- X;;; j forms-jump-record
- X;;; n forms-next-record
- X;;; p forms-prev-record
- X;;; q forms-exit
- X;;; s forms-search
- X;;; v forms-view-mode
- X;;; x forms-exit-no-save
- X;;; DEL forms-prev-record
- X;;;
- X;;; Standard functions scroll-up, scroll-down, beginning-of-buffer and
- X;;; end-of-buffer are wrapped with re-definitions, which map them to
- X;;; next/prev record and first/last record.
- X;;; Buffer-local variables forms-forms-scroll and forms-forms-jump
- X;;; may be used to control these redefinitions.
- X;;;
- X;;; Function save-buffer is also wrapped to perform a sensible action.
- X;;; A revert-file-hook is defined to revert a forms to original.
- X;;;
- X;;; For convenience, TAB is always bound to forms-next-field, so you
- X;;; don't need the C-c prefix for this command.
- X;;;
- X;;; Global variables and constants
- X
- X(defconst forms-version "1.1.2"
- X "Version of forms-mode implementation")
- X
- X(defvar forms-forms-scrolls t
- X "If non-null: redefine scroll-up/down to be used with forms-mode.")
- X
- X(defvar forms-forms-jumps t
- X "If non-null: redefine beginning/end-of-buffer to be used with forms-mode.")
- X
- X(defvar forms-mode-hooks nil
- X "Hook functions to be run upon entering forms mode.")
- X;;;
- X;;; Mandatory variables - must be set by evaluating the control file
- X
- X(defvar forms-file nil
- X "Name of the file holding the data.")
- X
- X(defvar forms-format-list nil
- X "Formatting specifications:
- X
- XIt should be a list, each element containing
- X
- X - either a string, e.g. "hello" (which is inserted \"as is\"),
- X
- X - an integer, denoting the number of a field which contents are
- X inserted at this point.
- X The first field has number one.
- X")
- X
- X(defvar forms-number-of-fields nil
- X "Number of fields per record.")
- X
- X;;;
- X;;; Optional variables with default values
- X
- X(defvar forms-field-sep "\t"
- X "Field separator character (default TAB)")
- X
- X(defvar forms-read-only nil
- X "Read-only mode (defaults to the write access on the data file).")
- X
- X(defvar forms-multi-line "\C-k"
- X "Character to separate multi-line fields (default ^K)")
- X
- X(defvar forms-forms-scroll t
- X "Redefine scroll-up/down to perform forms-next/prev-record when in
- X forms mode.")
- X
- X(defvar forms-forms-jump t
- X "Redefine beginning/end-of-buffer to perform forms-first/last-record
- X when in forms mode.")
- X
- X;;;
- X;;; Internal variables.
- X
- X(defvar forms--file-buffer nil
- X "Buffer which holds the file data")
- X
- X(defvar forms--total-records 0
- X "Total number of records in the data file.")
- X
- X(defvar forms--current-record 0
- X "Number of the record currently on the screen.")
- X
- X(defvar forms-mode-map nil ; yes - this one is global
- X "Keymap for form buffer.")
- X
- X(defvar forms--markers nil
- X "Field markers in the screen.")
- X
- X(defvar forms--number-of-markers 0
- X "Number of fields on screen.")
- X
- X(defvar forms--the-record-list nil
- X "List of strings of the current record, as parsed from the file.")
- X
- X(defvar forms--search-regexp nil
- X "Last regexp used by forms-search.")
- X
- X(defvar forms--format nil
- X "Formatting routine.")
- X
- X(defvar forms--parser nil
- X "Forms parser routine.")
- X
- X(defvar forms--mode-setup nil
- X "*Internal* - keeps track of forms-mode being set-up.")
- X(make-variable-buffer-local 'forms--mode-setup)
- X
- X;;;
- X;;; forms-mode
- X;;;
- X;;; This is not a simple major mode, as usual. Therefore, forms-mode
- X;;; takes an optional argument 'primary' which is used for the initial
- X;;; set-up. Normal use would leave 'primary' to nil.
- X;;;
- X;;; A global buffer-local variable 'forms--mode-setup' has the same effect
- X;;; but makes it possible to auto-invoke forms-mode using find-file.
- X;;;
- X;;; Note: although it seems logical to have (make-local-variable) executed
- X;;; where the variable is first needed, I deliberately placed all calls
- X;;; in the forms-mode function.
- X
- X(defun forms-mode (&optional primary)
- X "Major mode to visit files in a field-structured manner using a form.
- X
- X Commands (prefix with C-c if not in read-only mode):
- X \\{forms-mode-map}"
- X
- X (interactive) ; no - 'primary' is not prefix arg
- X
- X ;; Primary set-up: evaluate buffer and check if the mandatory
- X ;; variables have been set.
- X (if (or primary (not forms--mode-setup))
- X (progn
- X (kill-all-local-variables)
- X
- X ;; make mandatory variables
- X (make-local-variable 'forms-file)
- X (make-local-variable 'forms-number-of-fields)
- X (make-local-variable 'forms-format-list)
- X
- X ;; make optional variables
- X (make-local-variable 'forms-field-sep)
- X (make-local-variable 'forms-read-only)
- X (make-local-variable 'forms-multi-line)
- X (make-local-variable 'forms-forms-scroll)
- X (make-local-variable 'forms-forms-jump)
- X
- X ;; eval the buffer, should set variables
- X (eval-current-buffer)
- X
- X ;; check if the mandatory variables make sense.
- X (or forms-file
- X (error "'forms-file' has not been set"))
- X (or forms-number-of-fields
- X (error "'forms-number-of-fields' has not been set"))
- X (or (> forms-number-of-fields 0)
- X (error "'forms-number-of-fields' must be > 0")
- X (or (stringp forms-field-sep))
- X (error "'forms-field-sep' is not a string"))
- X (if forms-multi-line
- X (if (and (stringp forms-multi-line)
- X (eq (length forms-multi-line) 1))
- X (if (string= forms-multi-line forms-field-sep)
- X (error "'forms-multi-line' is equal to 'forms-field-sep'"))
- X (error "'forms-multi-line' must be nil or a one-character string")))
- X
- X ;; validate and process forms-format-list
- X (make-local-variable 'forms--number-of-markers)
- X (make-local-variable 'forms--markers)
- X (forms--process-format-list)
- X
- X ;; build the formatter and parser
- X (make-local-variable 'forms--format)
- X (forms--make-format)
- X (make-local-variable 'forms--parser)
- X (forms--make-parser)
- X
- X ;; prepare this buffer for further processing
- X (setq buffer-read-only nil)
- X
- X ;; prevent accidental overwrite of the control file
- X (setq buffer-file-name nil)
- X
- X ;; and clean it
- X (erase-buffer)))
- X
- X ;; make local variables
- X (make-local-variable 'forms--file-buffer)
- X (make-local-variable 'forms--total-records)
- X (make-local-variable 'forms--current-record)
- X (make-local-variable 'forms--the-record-list)
- X (make-local-variable 'forms--search-rexexp)
- X
- X ;; A bug in the current Emacs release 18.54 prevents a keymap
- X ;; which is buffer-local from being used by 'describe-mode'.
- X ;; Hence we'll leave it global.
- X ;;(make-local-variable 'forms-mode-map)
- X (if forms-mode-map ; already defined
- X nil
- X (setq forms-mode-map (make-keymap))
- X (forms--mode-commands forms-mode-map)
- X (forms--change-commands))
- X
- X ;; find the data file
- X (setq forms--file-buffer (find-file-noselect forms-file))
- X
- X ;; count the number of records, and set see if it may be modified
- X (let (ro)
- X (setq forms--total-records
- X (save-excursion
- X (set-buffer forms--file-buffer)
- X (bury-buffer (current-buffer))
- X (setq ro buffer-read-only)
- X (count-lines (point-min) (point-max))))
- X (if ro
- X (setq forms-read-only t)))
- X
- X ;; set the major mode indicator
- X (setq major-mode 'forms-mode)
- X (setq mode-name "Forms")
- X (make-local-variable 'minor-mode-alist) ; needed?
- X (forms--set-minor-mode)
- X (forms--set-keymaps)
- X
- X (set-buffer-modified-p nil)
- X
- X ;; We have our own revert function - use it
- X (make-local-variable 'revert-buffer-function)
- X (setq revert-buffer-function 'forms-revert-buffer)
- X
- X ;; setup the first (or current) record to show
- X (if (< forms--current-record 1)
- X (setq forms--current-record 1))
- X (forms-jump-record forms--current-record)
- X
- X ;; be helpful
- X (forms--help)
- X
- X ;; just in case
- X (run-hooks 'forms-mode-hooks)
- X
- X ;; initialization done
- X (setq forms--mode-setup t))
- X
- X;;;
- X;;; forms-process-format-list
- X;;;
- X;;; Validates forms-format-list.
- X;;;
- X;;; Sets forms--number-of-markers and forms--markers.
- X
- X(defun forms--process-format-list ()
- X "Validate forms-format-list and set some global variables."
- X
- X ;; it must be non-nil
- X (or forms-format-list
- X (error "'forms-format-list' has not been set"))
- X ;; it must be a list ...
- X (or (listp forms-format-list)
- X (error "'forms-format-list' is not a list"))
- X
- X (setq forms--number-of-markers 0)
- X
- X (let ((the-list forms-format-list) ; the list of format elements
- X (field-num 0)) ; highest field number
- X
- X (while the-list
- X
- X (let ((el (car-safe the-list))
- X (rem (cdr-safe the-list)))
- X
- X (cond
- X
- X ;; try string ...
- X ((stringp el)) ; string is OK
- X
- X ;; try int ...
- X ((numberp el) ; check it
- X
- X (if (or (<= el 0)
- X (> el forms-number-of-fields))
- X (error
- X "forms error: field number %d out of range 1..%d"
- X el forms-number-of-fields))
- X
- X (setq forms--number-of-markers (1+ forms--number-of-markers))
- X (if (> el field-num)
- X (setq field-num el)))
- X
- X ;; else
- X (t
- X (error "invalid element in 'forms-format-list': %s"
- X (prin1-to-string el)))
- X
- X ;; dead code - we'll need it in the future
- X ((consp el) ; check it
- X
- X (let ((str (car-safe el))
- X (idx (cdr-safe el)))
- X
- X (cond
- X
- X ;; car must be string
- X ((not (stringp str))
- X (error "forms error: car of cons %s must be string"
- X (prin1-to-string el)))
- X
- X ;; cdr must be number, > zero
- X ((or (not (numberp idx))
- X (<= idx 0)
- X (> idx forms-number-of-fields))
- X (error
- X "forms error: cdr of cons %s must be a number between 1 and %d"
- X (prin1-to-string el)
- X forms-number-of-fields)))
- X
- X ;; passed the test - handle it
- X (setq forms--number-of-markers (1+ forms--number-of-markers))
- X (if (> idx field-num)
- X (setq field-num idx)))))
- X
- X ;; advance to next element of the list
- X (setq the-list rem))))
- X
- X (setq forms--markers (make-vector forms--number-of-markers nil)))
- X
- X
- X;;;
- X;;; Build the format routine from forms-format-list.
- X;;;
- X;;; The format routine (forms--format) will look like
- X;;;
- X;;; (lambda (arg)
- X;;;
- X;;; ;; "text: "
- X;;; (insert "text: ")
- X;;; ;; 6
- X;;; (aset forms--markers 0 (point-marker))
- X;;; (insert (elt arg 5))
- X;;; ;; "\nmore text: "
- X;;; (insert "\nmore text: ")
- X;;; ;; 9
- X;;; (aset forms--markers 1 (point-marker))
- X;;; (insert (elt arg 8))
- X;;;
- X;;; ... )
- X;;;
- X
- X(defun forms--make-format ()
- X "Parse forms-format-list and build forms--format function"
- X (setq forms--format nil)
- X
- X (let ((the-list forms-format-list) ; the list of format elements
- X (the-result nil) ; the strings and elements
- X (pending-text nil) ; accumulated text
- X (the-marker 0)) ; number of current marker
- X
- X (while the-list
- X
- X (let ((el (car-safe the-list))
- X (rem (cdr-safe the-list)))
- X
- X (cond
- X
- X ((stringp el) ; element is a string
- X
- X (setq el (prin1-to-string el)) ; quote it
- X
- X (if (stringp pending-text) ; text is pending ...
- X (setq pending-text ; concatenate it
- X (concat (substring pending-text 0 -1)
- X (substring el 1)))
- X (setq pending-text el))) ; else set it
- X
- X ;; else ...
- X ((numberp el) ; number -> field id
- X
- X (if (stringp pending-text) ; text pending
- X (setq the-result
- X (concat the-result "(insert " pending-text ") ")))
- X (setq pending-text nil)
- X
- X (setq the-result
- X (concat the-result "(aset forms--markers "
- X the-marker " (point-marker)) "
- X "(insert (elt arg " (1- el) ")) "))
- X (setq the-marker (1+ the-marker))))
- X
- X ;; advance to next element of the list
- X (setq the-list rem)))
- X
- X (if (stringp pending-text) ; text pending
- X (setq the-result
- X (concat the-result "(insert " pending-text ") ")))
- X
- X ;; use the lisp reader to evalute the string to a function
- X (setq the-result (concat "(lambda (arg) " the-result ")"))
- X (let ((res (read-from-string the-result))
- X (len (length the-result)))
- X ;; has the whole string been parsed?
- X (if (= (cdr res) len)
- X (setq forms--format (car res))
- X ;; pity
- X (error "forms--make-format failed at %d [of %d]" (cdr res) len)))))
- X;;;
- X;;; forms--make-parser.
- X;;;
- X;;; Generate parse routine from forms-format-list.
- X;;;
- X;;; The parse routine (forms--parser) will look like (give or take
- X;;; a few " " .
- X;;;
- X;;; (lambda nil
- X;;; (let (here)
- X;;; (goto-char (point-min))
- X;;;
- X;;; ;; "text: "
- X;;; (if (not (looking-at "text: "))
- X;;; (error "parse error: cannot find \"text: \""))
- X;;; (forward-char 6) ; past "text: "
- X;;;
- X;;; ;; 6
- X;;; ;; "\nmore text: "
- X;;; (setq here (point))
- X;;; (if (not (search-forward "\nmore text: " nil t nil))
- X;;; (error "parse error: cannot find \"\\nmore text: \""))
- X;;; (aset the-recordv 5 (buffer-substring here (- (point) 12)))
- X;;; ...
- X;;; ...
- X;;; ;; final flush
- X;;; (aset the-recordv 7 (buffer-substring (point) (point-max)))
- X;;;
- X
- X(defun forms--make-parser ()
- X "Generate parser function for forms."
- X
- X (setq forms--parser nil)
- X
- X (let ((the-list) ; the list of format elements
- X (the-result nil) ; emerging function
- X (pending-field nil) ; pending element number
- X (pending-text nil) ; previous string element
- X (pending-length 0)) ; length of pending string
- X
- X ;; force flush of terminal string arguments
- X (setq the-list (append forms-format-list '(0)))
- X
- X (setq the-result "(lambda () (let (here) (goto-char (point-min)) ")
- X
- X (while the-list
- X
- X (let ((el (car-safe the-list))
- X (rem (cdr-safe the-list)))
- X
- X (cond
- X
- X ((stringp el) ; element is a string
- X
- X (if (stringp pending-text) ; text pending
- X (progn
- X (setq pending-length (+ pending-length (length el)))
- X (setq pending-text
- X (concat (substring pending-text 0 -1)
- X (substring (prin1-to-string
- X (regexp-quote el)) 1))))
- X (setq pending-length (length el))
- X (setq pending-text (prin1-to-string (regexp-quote el)))))
- X
- X ;; else ...
- X ((numberp el) ; it's a field id
- X
- X (if (and pending-field
- X (null pending-text)
- X (> el 0))
- X (setq the-result
- X (concat the-result
- X "(error \"parse error: "
- X "cannot parse adjacent fields "
- X pending-field
- X " and "
- X el
- X "\") ")))
- X
- X (if (stringp pending-text) ; text pending
- X
- X (if (null pending-field)
- X ; simple string match
- X (setq the-result
- X (concat the-result
- X "(if (not (looking-at " pending-text
- X ")) (error \"parse error: not looking at \\\""
- X (substring (prin1-to-string pending-text) 1 -1)
- X "\\\"\")) (forward-char "
- X pending-length
- X ") "))
- X
- X ;; else match using regexp and assign field
- X (setq the-result
- X (concat the-result
- X "(setq here (point)) "
- X "(if (not (search-forward "
- X pending-text
- X " nil t nil))"
- X "(error \"parse error: cannot find \\\""
- X (substring (prin1-to-string pending-text) 1 -1)
- X "\\\"\")) (aset the-recordv "
- X (1- pending-field)
- X " (buffer-substring here (- (point) "
- X pending-length
- X "))) ")))
- X
- X ;; else - no text, maybe a field?
- X (if pending-field
- SHAR_EOF
- echo "End of part 1"
- echo "File forms.el is continued in part 2"
- echo "2" > shar3_seq_.tmp
- exit 0
-
-